/**
 * SubClassOverrideConstraint.js
 * 提供装饰器函数，用于约束子类对父类方法的继承行为：
 * 1. abstract - 标记方法为抽象方法，要求子类必须重写
 * 2. mustBeCalledByChildren - 要求子类重写方法时必须调用父类同名方法
 * 
 * 使用方法：
 * 1. 导入装饰器：const { abstract, mustBeCalledByChildren } = require('./SubClassOverrideConstraint.js');
 * 2. 在支持装饰器语法的环境中，可以直接使用@abstract和@mustBeCalledByChildren语法
 * 3. 在Node.js或不支持装饰器的环境中，需要使用applyDecorators工具函数手动应用装饰器
 */

// 存储所有被mustBeCalledByChildren标记的方法信息
const mustCallParentMethods = new Map();

/**
 * 装饰器@abstract：确保子类必须重写该抽象方法
 */
function abstract(target, methodName, descriptor) {
  const originalMethod = descriptor.value;
  
  // 包装方法，在调用时检查是否被重写
  descriptor.value = function(...args) {
    // 检查当前实例的构造函数原型是否与目标原型相同
    // 如果相同，说明子类没有重写此方法
    if (this.constructor.prototype[methodName] === target[methodName]) {
      throw new Error(`抽象方法「${methodName}」必须被子类重写（@abstract标记）！`);
    }
    return originalMethod.apply(this, args);
  };
  
  // 标记为抽象方法
  descriptor.value._isAbstract = true;
  
  return descriptor;
}

/**
 * 装饰器@mustBeCalledByChildren：确保子类在重写方法时必须调用父类方法
 */
function mustBeCalledByChildren(target, methodName, descriptor) {
  const originalMethod = descriptor.value;
  const parentClassName = target.constructor.name || '父类';
  
  // 存储被标记方法的信息
  if (!mustCallParentMethods.has(target.prototype)) {
    mustCallParentMethods.set(target.prototype, new Map());
  }
  const parentMethods = mustCallParentMethods.get(target.prototype);
  parentMethods.set(methodName, {
    parentClassName,
    originalMethod
  });
  
  // 重写父类方法，添加调用标记
  descriptor.value = function(...args) {
    // 设置状态位，表示父类方法被调用
    const callFlag = `_parent_${methodName}_called`;
    this[callFlag] = true;
    return originalMethod.apply(this, args);
  };
  
  // 标记方法需要被子类调用
  descriptor.value._mustBeCalledByChildren = true;
  
  return descriptor;
}

/**
 * 应用装饰器到类方法
 * 这个函数直接确保子类方法被正确包装，不再依赖异步机制
 */
function applyDecorators(target, methodName, ...decorators) {
  // 获取方法描述符
  const descriptor = Object.getOwnPropertyDescriptor(target.prototype || target, methodName);
  if (!descriptor || typeof descriptor.value !== 'function') {
    throw new Error(`无法找到方法「${methodName}」或该方法不是函数`);
  }
  
  // 应用所有装饰器
  let newDescriptor = { ...descriptor };
  for (const decorator of decorators) {
    newDescriptor = decorator(target.prototype || target, methodName, newDescriptor) || newDescriptor;
  }
  
  // 更新方法
  Object.defineProperty(target.prototype || target, methodName, newDescriptor);
  
  // 如果应用了mustBeCalledByChildren装饰器，立即查找并包装所有已存在的子类
  if (decorators.includes(mustBeCalledByChildren) || 
      decorators.some(d => d.name === 'mustBeCalledByChildren')) {
    const parentClassName = target.name || '父类';
    immediateWrapExistingSubclasses(target, methodName, parentClassName);
  }
  
  // 返回target以支持链式调用
  return {
    applyDecorators: (nextMethodName, ...nextDecorators) => 
      applyDecorators(target, nextMethodName, ...nextDecorators)
  };
}

/**
 * 立即包装所有已存在的子类方法
 */
function immediateWrapExistingSubclasses(parentClass, methodName, parentClassName) {
  // 在Node.js环境中，我们可以扫描全局对象的构造函数
  // 获取全局对象
  const globalObj = typeof window !== 'undefined' ? window : 
                    typeof global !== 'undefined' ? global : 
                    typeof self !== 'undefined' ? self : {};
  
  // 遍历所有全局属性
  for (const key in globalObj) {
    try {
      const obj = globalObj[key];
      // 检查是否是构造函数且继承自父类
      if (typeof obj === 'function' && 
          obj.prototype && 
          obj !== parentClass && 
          obj.prototype instanceof parentClass) {
        
        // 检查子类是否重写了该方法
        if (obj.prototype.hasOwnProperty(methodName)) {
          const method = obj.prototype[methodName];
          if (typeof method === 'function' && !method._isWrapped) {
            // 创建包装器
            const wrapper = function(...args) {
              // 清除之前可能存在的状态位
              const callFlag = `_parent_${methodName}_called`;
              delete this[callFlag];
              
              // 执行子类方法
              const result = method.apply(this, args);
              
              // 检查是否调用了父类方法
              if (!(callFlag in this)) {
                throw new Error(`子类必须调用${parentClassName}的父类方法「${methodName}」`);
              }
              
              return result;
            };
            
            // 标记为已包装
            wrapper._isWrapped = true;
            
            // 复制原始方法的属性
            Object.defineProperties(wrapper, Object.getOwnPropertyDescriptors(method));
            
            // 替换子类方法
            Object.defineProperty(obj.prototype, methodName, {
              value: wrapper,
              configurable: true,
              writable: true,
              enumerable: false
            });
            
            console.log(`已包装子类 ${obj.name} 的方法 ${methodName}`);
          }
        }
      }
    } catch (e) {
      // 忽略无法访问的属性
    }
  }
  
  // 为了确保将来创建的子类也能被正确包装，我们需要重写类继承机制
  // 保存原始的Function.prototype
  if (!Function.prototype._originalExtends) {
    // 保存原始的Object.create方法
    const originalCreate = Object.create;
    
    // 重写Object.create以拦截原型创建
    Object.create = function(proto, propertiesObject) {
      const result = originalCreate(proto, propertiesObject);
      
      // 检查proto是否是某个被标记类的原型
      // 如果proto在mustCallParentMethods中有记录，说明它是一个被标记的父类原型
      if (mustCallParentMethods.has(proto)) {
        const parentMethods = mustCallParentMethods.get(proto);
        
        // 遍历所有被标记的方法
        for (const [method, info] of parentMethods.entries()) {
          // 检查是否有重写的方法
          if (result.hasOwnProperty(method)) {
            const childMethod = result[method];
            
            // 只处理函数类型的方法，并且避免重复包装
            if (typeof childMethod === 'function' && !childMethod._isWrapped) {
              // 创建包装器
              const wrapper = function(...args) {
                // 清除之前可能存在的状态位
                const callFlag = `_parent_${method}_called`;
                delete this[callFlag];
                
                // 执行子类方法
                const result = childMethod.apply(this, args);
                
                // 检查是否调用了父类方法
                if (!(callFlag in this)) {
                  throw new Error(`子类必须调用${info.parentClassName}的父类方法「${method}」`);
                }
                
                return result;
              };
              
              // 标记为已包装
              wrapper._isWrapped = true;
              
              // 复制原始方法的属性
              Object.defineProperties(wrapper, Object.getOwnPropertyDescriptors(childMethod));
              
              // 替换方法
              Object.defineProperty(result, method, {
                value: wrapper,
                configurable: true,
                writable: true,
                enumerable: false
              });
              
              console.log(`已拦截并包装通过Object.create创建的子类方法 ${method}`);
            }
          }
        }
      }
      
      return result;
    };
    
    // 标记已重写
    Function.prototype._originalExtends = true;
  }
}

/**
 * 工具函数：优雅地应用多个装饰器到类方法
 * @param {Function} targetClass - 目标类
 * @param {string} methodName - 方法名
 * @param {...Function} decorators - 装饰器函数列表
 * @returns {Object} 返回支持链式调用的对象
 */
function applyDecorators(targetClass, methodName, ...decorators) {
  // 获取原始方法
  if (!(methodName in targetClass.prototype)) {
    throw new Error(`方法 ${methodName} 在 ${targetClass.name} 类中不存在`);
  }
  
  // 获取或创建方法描述符
  const descriptor = Object.getOwnPropertyDescriptor(targetClass.prototype, methodName) || {
    value: targetClass.prototype[methodName],
    writable: true,
    configurable: true,
    enumerable: false
  };
  
  // 从右到左应用装饰器
  let currentDescriptor = descriptor;
  for (let i = decorators.length - 1; i >= 0; i--) {
    const decorator = decorators[i];
    if (typeof decorator !== 'function') {
      throw new Error(`装饰器必须是函数，收到的是: ${typeof decorator}`);
    }
    const newDescriptor = decorator(targetClass.prototype, methodName, currentDescriptor);
    currentDescriptor = newDescriptor || currentDescriptor;
  }
  
  // 重新定义方法
  Object.defineProperty(targetClass.prototype, methodName, currentDescriptor);
  
  // 如果应用了mustBeCalledByChildren装饰器，立即查找并包装所有已存在的子类
  if (decorators.includes(mustBeCalledByChildren) || 
      decorators.some(d => d.name === 'mustBeCalledByChildren')) {
    const parentClassName = targetClass.name || '父类';
    immediateWrapExistingSubclasses(targetClass, methodName, parentClassName);
  }
  
  // 返回支持链式调用的对象
  return {
    applyDecorators: (nextMethodName, ...nextDecorators) => 
      applyDecorators(targetClass, nextMethodName, ...nextDecorators)
  };
}

// 导出装饰器和工具函数
module.exports = {
  abstract,
  mustBeCalledByChildren,
  applyDecorators
};